package main

import (
	"bytes"
	"context"
	"fmt"
	"gin-reverse-proxy-redis-cache/utils"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
	"log"
	"log/slog"
	"net/http"
	"time"
)

var rds *redis.Client
var switcher *utils.RoundRobinProxySwitcher

const Address = "192.168.1.112"
const Password = "qqqAAA0130"

func init() {
	rds = redis.NewClient(&redis.Options{
		Addr:     Address,
		Password: Password,
		DB:       0,
		PoolSize: 50,
	})
	err := rds.Ping(context.Background()).Err()
	if err != nil {
		log.Fatalln("redis 连接失败！")
		return
	}
	proxyAddress := []string{
		"http://192.168.1.114:37070",
	}
	switcher = utils.NewRoundRobinProxySwitcher(proxyAddress)
}

func main() {
	r := gin.Default()
	r.Use(proxyHandler)
	log.Fatalln(r.Run(fmt.Sprintf(":%d", "")))

}
func proxyHandler(ctx *gin.Context) {
	var responseBodyBuffer bytes.Buffer
	var header = make(map[string]string)
	statusCode := http.StatusOK
	address := switcher.GetProxy()
	url := ctx.Request.URL.RequestURI()
	domain := ctx.GetHeader("X-HOST")
	cacheKey := fmt.Sprintf("%s:%s", domain, url)
	if result := rds.Get(context.Background(), cacheKey); result.Err() == nil {
		body, _ := result.Bytes()
		rc := utils.ResponseCache{}
		rc.Encode(body)
		responseBodyBuffer.Write(rc.Body)
		header = rc.Header
		slog.Info(fmt.Sprintf("[%s] 命中缓存!", cacheKey))
	} else {
		// 发送代理请求
		res, err := utils.Client.NewRequest().SetHeaderMultiValues(ctx.Request.Header).Get(address + url)
		if err != nil {
			ctx.AbortWithStatus(http.StatusServiceUnavailable)
			return
		}
		responseBodyBuffer.Write(res.Body())
		headers := res.Header()
		statusCode = res.StatusCode()
		for key, value := range headers {
			if len(value) < 1 {
				continue
			}
			header[key] = value[0]
		}
		if res.StatusCode() == 200 {
			// 设置缓存
			response := utils.ResponseCache{Header: header, Body: res.Body()}
			if decode, ok := response.Decode(); ok {
				slog.Info(fmt.Sprintf("[%s] 缓存成功!", cacheKey))
				rds.Set(context.Background(), cacheKey, decode, time.Hour*48)
			}
		}
	}
	// 写入响应头
	for key, value := range header {
		ctx.Header(key, value)
	}
	// 设置状态码
	ctx.Status(statusCode)
	// 写入body
	responseBodyBuffer.WriteTo(ctx.Writer)
	ctx.Abort()
	return
}
